1 /*
2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021
3 License:   [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License].
4 Authors: Marcelo S. N. Mancini
5 
6 	Copyright Marcelo S. N. Mancini 2018 - 2021.
7 Distributed under the CC BY-4.0 License.
8    (See accompanying file LICENSE.txt or copy at
9 	https://creativecommons.org/licenses/by/4.0/
10 */
11 module hip.hiprenderer.backend.gl.glvertex;
12 version(OpenGL):
13 
14 import hip.hiprenderer.backend.gl.glrenderer;
15 import hip.error.handler;
16 import hip.util.conv;
17 import hip.config.opts;
18 import hip.hiprenderer.renderer;
19 import hip.hiprenderer.shader;
20 import hip.hiprenderer.vertex;
21 
22 
23 private int getGLUsage(HipBufferUsage usage)
24 {
25     final switch(usage) with(HipBufferUsage)
26     {
27         case STATIC:
28             return GL_STATIC_DRAW;
29         case DEFAULT:
30         case DYNAMIC:
31             return GL_DYNAMIC_DRAW;
32     }
33 }
34 private int getGLAttributeType(HipAttributeType _t)
35 {
36     final switch(_t) with(HipAttributeType)
37     {
38         case Rgba32: return GL_UNSIGNED_BYTE;
39         case Float: return GL_FLOAT;
40         case Int: return GL_INT;
41         case Uint: return GL_UNSIGNED_INT;
42         case Bool: return GL_BOOL;
43     }
44 }
45 
46 private ubyte isGLAttributeNormalized(HipAttributeType _t)
47 {
48     final switch(_t) with(HipAttributeType)
49     {
50         case Rgba32: return GL_TRUE;
51         case Float: return GL_FALSE;
52         case Int: return GL_FALSE;
53         case Uint: return GL_FALSE;
54         case Bool: return GL_FALSE;
55     }
56 }
57 
58 
59 
60 
61 class Hip_GL3_VertexBufferObject : IHipVertexBufferImpl
62 {
63     immutable int  usage;
64     size_t size;
65     uint vbo;
66 
67     private __gshared Hip_GL3_VertexBufferObject boundVbo;
68 
69     this(size_t size, HipBufferUsage usage)
70     {
71         this.size = size;
72         this.usage = getGLUsage(usage);
73         glCall(() => glGenBuffers(1, &this.vbo));
74     }
75     void bind()
76     {
77         if(boundVbo !is this)
78         {
79             glCall(()=>glBindBuffer(GL_ARRAY_BUFFER, this.vbo));
80             boundVbo = this;
81         }
82     }
83     void unbind()
84     {
85         if(boundVbo is this)
86         {
87             glCall(()=>glBindBuffer(GL_ARRAY_BUFFER, 0));
88             boundVbo = null;
89         }
90     }
91     void setData(const(void)[] data)
92     {
93         this.size = data.length;
94         this.bind();
95         glCall(() => glBufferData(GL_ARRAY_BUFFER, data.length, cast(void*)data.ptr, this.usage));
96     }
97     void updateData(int offset, const(void)[] data)
98     {
99         if(data.length + offset > this.size)
100         {
101             ErrorHandler.assertExit(
102                 false, "Tried to set data with size "~to!string(size)~"and offset "~to!string(offset)~
103         "for vertex buffer with size "~to!string(this.size));
104         }
105         this.bind();
106         glCall(() => glBufferSubData(GL_ARRAY_BUFFER, offset, data.length, data.ptr));
107     }
108     ~this(){glCall(() => glDeleteBuffers(1, &this.vbo));}
109 }
110 class Hip_GL3_IndexBufferObject : IHipIndexBufferImpl
111 {
112     immutable int  usage;
113     size_t size;
114     index_t count;
115     uint ebo;
116 
117     private __gshared Hip_GL3_IndexBufferObject boundEbo;
118 
119     this(index_t count, HipBufferUsage usage)
120     {
121         this.size = index_t.sizeof*count;
122         this.count = count;
123         this.usage = getGLUsage(usage);
124         glCall(() => glGenBuffers(1, &this.ebo));
125     }
126     void bind()
127     {
128         if(boundEbo !is this)
129         {
130             glCall(() => glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this.ebo));
131             boundEbo = this;
132         }
133     }
134     void unbind()
135     {
136         if(boundEbo is this)
137         {
138             glCall(() => glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
139             boundEbo = null;
140         }
141     }
142     void setData(const index_t[] data)
143     {
144         this.count = cast(index_t)data.length;
145         this.size = index_t.sizeof*data.length;
146         this.bind();
147         glCall(() => glBufferData(GL_ELEMENT_ARRAY_BUFFER, index_t.sizeof*data.length, cast(void*)data.ptr, this.usage));
148     }
149     void updateData(int offset, const index_t[] data)
150     {
151         ErrorHandler.assertExit((offset+data.length)*index_t.sizeof <= this.size);
152         this.bind();
153         glCall(() => glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, data.length*index_t.sizeof, cast(void*)data.ptr));
154     }
155     ~this(){glCall(() => glDeleteBuffers(1, &this.ebo));}
156 }
157 
158 //Used as a wrapper 
159 class Hip_GL_VertexArrayObject : IHipVertexArrayImpl
160 {
161     import hip.util.data_structures;
162     IHipVertexBufferImpl vbo;
163     IHipIndexBufferImpl ebo;
164     private alias VAOInfo = Pair!(HipVertexAttributeInfo, uint, "info", "stride");
165     VAOInfo[] vaoInfos;
166 
167     bool isWaitingCreation = false;
168 
169     private __gshared Hip_GL_VertexArrayObject boundVAO;
170 
171     void bind(IHipVertexBufferImpl vbo, IHipIndexBufferImpl ebo)
172     {
173         if(vbo is null)
174         {
175             isWaitingCreation = true;
176             return;
177         }
178         else
179             this.vbo = vbo;
180         if(ebo is null)
181         {
182             isWaitingCreation = true;
183             return;
184         }
185         else
186             this.ebo = ebo;
187         isWaitingCreation = false;
188         if(boundVAO !is this)
189         {
190             vbo.bind();
191             ebo.bind();
192             foreach(vao; vaoInfos)
193             {
194                 glCall(() => glEnableVertexAttribArray(vao.info.index));
195                 glCall(() => glVertexAttribPointer(
196                     vao.info.index,
197                     vao.info.count,
198                     getGLAttributeType(vao.info.valueType),
199                     isGLAttributeNormalized(vao.info.valueType),
200                     vao.stride,
201                     cast(void*)vao.info.offset
202                 ));
203             }
204             boundVAO = this;
205         }
206     }
207         
208     void unbind(IHipVertexBufferImpl vbo, IHipIndexBufferImpl ebo)
209     {
210         if(boundVAO is this)
211         {
212             foreach(vao; vaoInfos)
213             {
214                 glCall(() => glDisableVertexAttribArray(vao.info.index));
215             }
216             vbo.unbind();
217             ebo.unbind();
218             boundVAO = null;
219         }
220     }
221 
222     void setAttributeInfo(ref HipVertexAttributeInfo info, uint stride)
223     {
224         if(info.index + 1 > vaoInfos.length)
225             vaoInfos.length = info.index + 1;
226         vaoInfos[info.index] = VAOInfo(info, stride);
227     }
228     void createInputLayout(Shader s){}
229 }
230 
231 version(HipGLUseVertexArray) class Hip_GL3_VertexArrayObject : IHipVertexArrayImpl
232 {
233     uint vao;
234     private __gshared Hip_GL3_VertexArrayObject boundVao;
235     this()
236     {
237         glCall(() => glGenVertexArrays(1, &this.vao));
238     }
239     void bind(IHipVertexBufferImpl vbo, IHipIndexBufferImpl ebo)
240     {
241         if(boundVao !is this)
242         {
243             glCall(() => glBindVertexArray(this.vao));
244             boundVao = this;
245         }
246     }
247     void unbind(IHipVertexBufferImpl vbo, IHipIndexBufferImpl ebo)
248     {
249         if(boundVao is this)
250         {
251             glCall(() => glBindVertexArray(0));
252             boundVao = null;
253         }
254     }
255     void setAttributeInfo(ref HipVertexAttributeInfo info, uint stride)
256     {
257         glCall(() => glVertexAttribPointer(
258             info.index,
259             info.count, 
260             getGLAttributeType(info.valueType),
261             isGLAttributeNormalized(info.valueType),
262             stride,
263             cast(void*)info.offset
264         ));
265         glCall(() => glEnableVertexAttribArray(info.index));
266     }
267     void createInputLayout(Shader s){}
268     ~this(){glCall(() => glDeleteVertexArrays(1, &this.vao));}
269 }